﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace Fadd.Components
{
	/// <summary>
	/// None of the constructors provided could be used.
	/// </summary>
	public class ConstructorMismatchException : Exception
	{
		private readonly Type _type;
		private readonly List<ConstructorParameters> _constructors = new List<ConstructorParameters>();

		/// <summary>
		/// Initializes a new instance of the <see cref="ConstructorMismatchException"/> class.
		/// </summary>
		/// <param name="type">Type that no constructor was found for.</param>
		public ConstructorMismatchException(Type type) : base("Could not find a suitable constructor for '" + type.FullName + "'.")
		{
			_type = type;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ConstructorMismatchException"/> class.
		/// </summary>
		/// <param name="type">Type that no constructor was found for.</param>
		/// <param name="constructors">All scanned constructors.</param>
		public ConstructorMismatchException(Type type, List<ConstructorParameters> constructors)
			: base("Could not find a suitable constructor for '" + type.FullName + "'.")
		{
			_type = type;
			_constructors = constructors;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ConstructorMismatchException"/> class.
		/// </summary>
		public ConstructorMismatchException(Type type, Exception err)
			: base("Could not find a suitable constructor for '" + type.FullName + "', one of its dependencies could not be created. Check inner exception for more information.", err)
		{
			_type = type;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ConstructorMismatchException"/> class.
		/// </summary>
		/// <param name="type">Type that no constructor was found for.</param>
		/// <param name="constructors">All scanned constructors.</param>
		/// <param name="err">Inner exception.</param>
		public ConstructorMismatchException(Type type, List<ConstructorParameters> constructors, Exception err)
			: base("Could not find a suitable constructor for '" + type.FullName + "'.", err)
		{
			_type = type;
			_constructors = constructors;
		}

		/// <summary>
		/// Adds the specified constructor.
		/// </summary>
		/// <param name="constructor">The constructor.</param>
		public void Add(ConstructorParameters constructor)
		{
			_constructors.Add(constructor);
		}

		/// <summary>
		/// Gets a message that describes the current exception.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The error message that explains the reason for the exception, or an empty string("").
		/// </returns>
		public override string Message
		{
			get
			{
				if (InnerException is ConstructorMismatchException)
					return "Failed to find suitable constructor, failed to retrieve depedent type: \r\n" + InnerException.Message;

				if (_constructors.Count == 0)
					return _type.FullName + " do not have any public constructors.";
				string msg = base.Message + "\r\n";
				foreach (var parameters in _constructors)
					msg += parameters + "\r\n";
				return msg;
			}
		}
	}

	/// <summary>
	/// A constructor parameter.
	/// </summary>
	public class ConstructorParameter
	{
		/// <summary>
		/// Gets name of parameter.
		/// </summary>
		public string Name { get; internal set; }

		/// <summary>
		/// Gets whether the parameter is missing.
		/// </summary>
		public bool IsMissing { get { return Instance == null; } }

		internal ParameterInfo Parameter
		{
			get; set;
		}

		internal object Instance
		{ get; set; }
	}

	/// <summary>
	/// All parameters for a constructor.
	/// </summary>
	public class ConstructorParameters : IEnumerable<ConstructorParameter>
	{
		private readonly List<ConstructorParameter> _parameters = new List<ConstructorParameter>();

		/// <summary>
		/// Gets or sets name of class that the constructor belongs to.
		/// </summary>
		public string TypeName { get; set; }

		/// <summary>
		/// Add a constructor parameter.
		/// </summary>
		/// <param name="parameter"></param>
		public void Add(ConstructorParameter parameter)
		{
			_parameters.Add(parameter);
		}


		/// <summary>
		///                     Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		///                     A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>1</filterpriority>
		public IEnumerator<ConstructorParameter> GetEnumerator()
		{
			return _parameters.GetEnumerator();
		}

		/// <summary>
		/// Show parameters.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			string str = TypeName ?? string.Empty;
			foreach (var parameter in _parameters)
			{
				str += parameter.Name;
				if (parameter.IsMissing)
					str += " [missing]";
				str += ", ";
			}

			return str.Remove(str.Length - 2);
		}

		/// <summary>
		///                     Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		///                     An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
}
